package health_system.health_system.controller;

import health_system.health_system.dto.*;
import health_system.health_system.entity.LoginRecord;
import health_system.health_system.entity.RefreshToken;
import health_system.health_system.entity.User;
import health_system.health_system.exception.BusinessException;
import health_system.health_system.repository.UserRepository;
import health_system.health_system.security.JwtTokenProvider;
import health_system.health_system.service.*;
import jakarta.servlet.http.Cookie;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.bind.annotation.*;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;

/**
 * Controller for handling authentication related requests
 */
@Slf4j
@RestController
@RequestMapping("/api/auth")
@RequiredArgsConstructor
public class AuthController {

    private final UserService userService;
    private final AuthenticationManager authenticationManager;
    private final JwtTokenProvider tokenProvider;
    private final UserRepository userRepository;
    private final LoginRecordService loginRecordService;
    private final FriendshipService friendshipService;
    private final RefreshTokenService refreshTokenService;

    /**
     * Process login request
     * POST /api/auth/login
     *
     * @param loginRequest the login credentials
     * @param response HTTP response to set cookie
     * @param request HTTP request to get client info
     * @return authentication result with user data and JWT token
     */
    @PostMapping("/login")
    public ResponseEntity<?> login(
            @Valid @RequestBody LoginRequest loginRequest,
            HttpServletResponse response,
            HttpServletRequest request) {

        log.debug("REST request to login user with username: {}", loginRequest.getUsername());
        try {
            // Try to find user to record login activity
            User user = userRepository.findByUsername(loginRequest.getUsername())
                    .orElse(null);

            try {
                // Attempt authentication
                Authentication authentication = authenticationManager.authenticate(
                        new UsernamePasswordAuthenticationToken(
                                loginRequest.getUsername(),
                                loginRequest.getPassword()
                        )
                );
                SecurityContextHolder.getContext().setAuthentication(authentication);
                
                // Generate JWT access token
                String accessToken = tokenProvider.createToken(authentication);
                
                // Add token to cookie for web clients
                tokenProvider.addCookieToResponse(response, accessToken);
                
                // Get authenticated user details
                UserDTO userDTO = userService.getUserByUsername(loginRequest.getUsername());
                
                // Get user's friends
                List<UserDTO> friends = Collections.emptyList();
                if (user != null) {
                    friends = friendshipService.getFriendsByUser(user.getId());
                }
                
                // Record successful login
                if (user != null) {
                    loginRecordService.recordLogin(
                            user,
                            request,
                            LoginRecord.LoginStatus.SUCCESS,
                            null
                    );
                    
                    // Check for suspicious activity
                    boolean isSuspicious = loginRecordService.detectSuspiciousActivity(
                            user.getId(),
                            request.getRemoteAddr()
                    );
                    
                    if (isSuspicious) {
                        log.warn("Suspicious login detected for user {}", loginRequest.getUsername());
                        // Additional security measures can be added here
                    }
                }
                
                // Create refresh token (especially for mobile clients)
                RefreshToken refreshToken = null;
                if (user != null) {
                    refreshToken = refreshTokenService.createRefreshToken(
                            user.getId(),
                            loginRequest.getDeviceId(),
                            loginRequest.getDeviceType(),
                            request.getRemoteAddr()
                    );
                }
                
                // Build response
                JwtAuthResponse authResponse = JwtAuthResponse.builder()
                        .accessToken(accessToken)
                        .tokenType("Bearer")
                        .user(userDTO)
                        .friends(friends)
                        .expiresIn(tokenProvider.getAccessTokenExpiration())
                        .build();
                
                // Add refresh token for mobile clients
                if (refreshToken != null) {
                    authResponse.setRefreshToken(refreshToken.getToken());
                }
                
                return ResponseEntity.ok(ApiResponse.success("Login successful", authResponse));
                
            } catch (BadCredentialsException e) {
                // Wrong password
                if (user != null) {
                    loginRecordService.recordLogin(
                            user,
                            request,
                            LoginRecord.LoginStatus.FAILED,
                            "Invalid credentials"
                    );
                }

                log.error("Invalid credentials for username: {}", loginRequest.getUsername());
                return ResponseEntity.status(HttpStatus.UNAUTHORIZED)
                        .body(ApiResponse.error(HttpStatus.UNAUTHORIZED.value(), "Invalid username or password"));
                
            } catch (AuthenticationException e) {
                // Other authentication exceptions
                if (user != null) {
                    loginRecordService.recordLogin(
                            user,
                            request,
                            LoginRecord.LoginStatus.FAILED,
                            "Authentication exception: " + e.getMessage()
                    );
                }

                log.error("Authentication failed for username: {}", loginRequest.getUsername(), e);
                return ResponseEntity.status(HttpStatus.UNAUTHORIZED)
                        .body(ApiResponse.error(HttpStatus.UNAUTHORIZED.value(), "Authentication failed: " + e.getMessage()));
            }
        } catch (Exception e) {
            log.error("Login failed for username: {}", loginRequest.getUsername(), e);
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
                    .body(ApiResponse.error(HttpStatus.INTERNAL_SERVER_ERROR.value(), "An error occurred during login"));
        }
    }

    /**
     * Process registration request
     * POST /api/auth/register
     *
     * @param registerDTO the user creation data
     * @param response HTTP response to set cookie
     * @param request HTTP request to get client info
     * @return registration result with user data and JWT token
     */
    @PostMapping("/register")
    public ResponseEntity<?> register(
            @Valid @RequestBody UserCreateDTO registerDTO,
            HttpServletResponse response,
            HttpServletRequest request) {
            
        log.debug("REST request to register new user: {}", registerDTO);
        log.info("Registration request received with fields: username={}, fullName={}, email={}, phoneNumber={}, gender={}, dateOfBirth={}, deviceId={}, deviceType={}",
                registerDTO.getUsername(),
                registerDTO.getFullName(),
                registerDTO.getEmail(),
                registerDTO.getPhoneNumber(),
                registerDTO.getGender(),
                registerDTO.getDateOfBirth(),
                registerDTO.getDeviceId(),
                registerDTO.getDeviceType());

        try {
            // Check if the user already exists
            if (userService.existsByUsername(registerDTO.getUsername())) {
                return ResponseEntity.badRequest()
                        .body(ApiResponse.error(HttpStatus.BAD_REQUEST.value(), "Username is already taken"));
            }
            
            if (userService.existsByEmail(registerDTO.getEmail())) {
                return ResponseEntity.badRequest()
                        .body(ApiResponse.error(HttpStatus.BAD_REQUEST.value(), "Email is already in use"));
            }
            
            // By default, all new users are regular users (not admin, not moderator)
//            registerDTO.setAdmin(false);
//            registerDTO.setModerator(false);
            
            // Create the user
            UserDTO result = userService.createUser(registerDTO);

            // 创建适当的权限列表
            List<SimpleGrantedAuthority> authorities = new ArrayList<>();
            authorities.add(new SimpleGrantedAuthority("USER"));
            
//            if (result.isAdmin()) {
//                authorities.add(new SimpleGrantedAuthority("ADMIN"));
//            }
//
//            if (result.isModerator()) {
//                authorities.add(new SimpleGrantedAuthority("MODERATOR"));
//            }

            // 生成JWT令牌
            String authoritiesString = authorities.stream()
                    .map(SimpleGrantedAuthority::getAuthority)
                    .reduce((a, b) -> a + "," + b)
                    .orElse("USER");
                    
            String accessToken = tokenProvider.createTokenForUser(result.getUsername(), authoritiesString);

            // Add the token as a cookie for web clients
            tokenProvider.addCookieToResponse(response, accessToken);

            // Setup Spring Security context for automatic login
            Authentication authentication = new UsernamePasswordAuthenticationToken(
                    result.getUsername(),
                    null, // no credentials since we've already verified the user
                    authorities
            );
            SecurityContextHolder.getContext().setAuthentication(authentication);
            
            // Create refresh token for mobile clients
            RefreshToken refreshToken = refreshTokenService.createRefreshToken(
                    result.getId(),
                    registerDTO.getDeviceId(),
                    registerDTO.getDeviceType(),
                    request.getRemoteAddr()
            );

            // Build response
            JwtAuthResponse authResponse = JwtAuthResponse.builder()
                    .accessToken(accessToken)
                    .refreshToken(refreshToken.getToken())
                    .tokenType("Bearer")
                    .user(result)
                    .expiresIn(tokenProvider.getAccessTokenExpiration())
                    .build();

            return ResponseEntity.status(HttpStatus.CREATED)
                    .body(ApiResponse.success("Registration successful", authResponse));
                    
        } catch (Exception e) {
            log.error("Registration failed", e);
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
                    .body(ApiResponse.error(HttpStatus.INTERNAL_SERVER_ERROR.value(), "Registration failed: " + e.getMessage()));
        }
    }

    /**
     * Process mobile registration request with simplified validation
     * POST /api/auth/mobile/register
     *
     * @param mobileRegisterRequest the simplified user creation data
     * @param response HTTP response to set cookie
     * @param request HTTP request to get client info
     * @return registration result with user data and JWT token
     */
    @PostMapping("/mobile/register")
    public ResponseEntity<?> mobileRegister(
            @RequestBody Map<String, Object> mobileRegisterRequest,
            HttpServletResponse response,
            HttpServletRequest request) {
            
        log.info("Mobile registration request received: {}", mobileRegisterRequest);
        
        try {
            // Extract fields from the request
            String username = (String) mobileRegisterRequest.get("username");
            String password = (String) mobileRegisterRequest.get("password");
            String fullName = (String) mobileRegisterRequest.get("fullName");
            String email = (String) mobileRegisterRequest.get("email");
            String phoneNumber = (String) mobileRegisterRequest.get("phoneNumber");
            String genderStr = (String) mobileRegisterRequest.get("gender");
            String deviceId = (String) mobileRegisterRequest.get("deviceId");
            String deviceType = (String) mobileRegisterRequest.get("deviceType");
            
            // Validate required fields
            if (username == null || username.isBlank()) {
                return ResponseEntity.badRequest()
                        .body(ApiResponse.error(HttpStatus.BAD_REQUEST.value(), "Username is required"));
            }
            
            if (password == null || password.isBlank()) {
                return ResponseEntity.badRequest()
                        .body(ApiResponse.error(HttpStatus.BAD_REQUEST.value(), "Password is required"));
            }
            
            if (fullName == null || fullName.isBlank()) {
                fullName = username; // Default to username if full name is not provided
            }
            
            // Create DTO
            UserCreateDTO registerDTO = new UserCreateDTO();
            registerDTO.setUsername(username);
            registerDTO.setPassword(password);
            registerDTO.setFullName(fullName);
            registerDTO.setEmail(email);
            registerDTO.setPhoneNumber(phoneNumber);
            registerDTO.setDeviceId(deviceId);
            registerDTO.setDeviceType(deviceType);
            
            // Parse gender
            if (genderStr != null && !genderStr.isBlank()) {
                try {
                    registerDTO.setGender(User.Gender.valueOf(genderStr.toUpperCase()));
                } catch (IllegalArgumentException e) {
                    // Default to OTHER if invalid gender provided
                    registerDTO.setGender(User.Gender.OTHER);
                }
            } else {
                registerDTO.setGender(User.Gender.OTHER); // Default gender
            }
            
            // Check if the user already exists
            if (userService.existsByUsername(registerDTO.getUsername())) {
                return ResponseEntity.badRequest()
                        .body(ApiResponse.error(HttpStatus.BAD_REQUEST.value(), "Username is already taken"));
            }
            
            if (registerDTO.getEmail() != null && !registerDTO.getEmail().isBlank() && userService.existsByEmail(registerDTO.getEmail())) {
                return ResponseEntity.badRequest()
                        .body(ApiResponse.error(HttpStatus.BAD_REQUEST.value(), "Email is already in use"));
            }
            
            // By default, all new users are regular users (not admin, not moderator)
//            registerDTO.setAdmin(false);
//            registerDTO.setModerator(false);
            
            // Create the user
            UserDTO result = userService.createUser(registerDTO);

            // Create authorities
            List<SimpleGrantedAuthority> authorities = new ArrayList<>();
            authorities.add(new SimpleGrantedAuthority("USER"));
            
            // Generate JWT token
            String authoritiesString = "USER";
            String accessToken = tokenProvider.createTokenForUser(result.getUsername(), authoritiesString);

            // Create refresh token for mobile clients
            RefreshToken refreshToken = refreshTokenService.createRefreshToken(
                    result.getId(),
                    registerDTO.getDeviceId(),
                    registerDTO.getDeviceType(),
                    request.getRemoteAddr()
            );

            // Build response
            JwtAuthResponse authResponse = JwtAuthResponse.builder()
                    .accessToken(accessToken)
                    .refreshToken(refreshToken.getToken())
                    .tokenType("Bearer")
                    .user(result)
                    .expiresIn(tokenProvider.getAccessTokenExpiration())
                    .build();

            return ResponseEntity.status(HttpStatus.CREATED)
                    .body(ApiResponse.success("Registration successful", authResponse));
                    
        } catch (Exception e) {
            log.error("Mobile registration failed", e);
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
                    .body(ApiResponse.error(HttpStatus.INTERNAL_SERVER_ERROR.value(), "Registration failed: " + e.getMessage()));
        }
    }

    /**
     * Get current authenticated user details
     * GET /api/auth/me
     *
     * @return current user details
     */
    @GetMapping("/me")
    public ResponseEntity<?> getCurrentUser() {
        log.debug("REST request to get current user");

        try {
            // Get username from the security context
            Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
            String username = authentication.getName();

            UserDTO currentUser = userService.getUserByUsername(username);
            return ResponseEntity.ok(ApiResponse.success("User details retrieved successfully", currentUser));
        } catch (Exception e) {
            log.error("Failed to get current user", e);
            return ResponseEntity.status(HttpStatus.UNAUTHORIZED)
                    .body(ApiResponse.error(HttpStatus.UNAUTHORIZED.value(), "Invalid or expired token"));
        }
    }

    /**
     * Process logout request
     * POST /api/auth/logout
     *
     * @param request HTTP request to get device info
     * @param response HTTP response to clear cookie
     * @return logout confirmation
     */
    @PostMapping("/logout")
    public ResponseEntity<?> logout(
            HttpServletRequest request,
            HttpServletResponse response,
            @RequestBody(required = false) Map<String, String> logoutRequest) {
            
        try {
            // Clear the JWT cookie for web clients
            tokenProvider.deleteCookieFromResponse(response);
            
            // Revoke refresh token for mobile clients if provided
            String refreshToken = logoutRequest != null ? logoutRequest.get("refreshToken") : null;
            String deviceId = logoutRequest != null ? logoutRequest.get("deviceId") : null;
            
            Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
            if (authentication != null && authentication.isAuthenticated()) {
                String username = authentication.getName();
                User user = userRepository.findByUsername(username).orElse(null);
                
                if (user != null) {
                    if (refreshToken != null && !refreshToken.isEmpty()) {
                        // Revoke the specific refresh token
                        refreshTokenService.findByToken(refreshToken)
                                .ifPresent(refreshTokenService::deleteToken);
                    } else if (deviceId != null && !deviceId.isEmpty()) {
                        // Revoke all tokens for this device
                        refreshTokenService.revokeUserTokensForDevice(user, deviceId);
                    }
                }
            }

            // Clear security context
            SecurityContextHolder.clearContext();

            return ResponseEntity.ok(ApiResponse.success("Logout successful"));
        } catch (Exception e) {
            log.error("Logout failed", e);
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
                    .body(ApiResponse.error(HttpStatus.INTERNAL_SERVER_ERROR.value(), "Logout failed: " + e.getMessage()));
        }
    }
    
    /**
     * Refresh access token using refresh token
     * POST /api/auth/refresh
     *
     * @param refreshTokenRequest the refresh token request
     * @param request HTTP request to get client info
     * @return new access token and refresh token
     */
    @PostMapping("/refresh")
    public ResponseEntity<?> refreshToken(
            @Valid @RequestBody RefreshTokenRequest refreshTokenRequest,
            HttpServletRequest request,
            HttpServletResponse response) {
            
        try {
            String requestRefreshToken = refreshTokenRequest.getRefreshToken();
            
            return refreshTokenService.findByToken(requestRefreshToken)
                    .map(refreshTokenService::verifyExpiration)
                    .map(RefreshToken::getUser)
                    .map(user -> {
                        // 创建适当的权限列表
                        List<SimpleGrantedAuthority> authorities = new ArrayList<>();
                        authorities.add(new SimpleGrantedAuthority("USER"));
                        
                        if (user.isAdmin()) {
                            authorities.add(new SimpleGrantedAuthority("ADMIN"));
                        }
                        
                        if (user.isModerator()) {
                            authorities.add(new SimpleGrantedAuthority("MODERATOR"));
                        }
                        
                        // 生成JWT令牌
                        String authoritiesString = authorities.stream()
                                .map(SimpleGrantedAuthority::getAuthority)
                                .reduce((a, b) -> a + "," + b)
                                .orElse("USER");
                        
                        // Generate new access token
                        String accessToken = tokenProvider.createTokenForUser(
                                user.getUsername(),
                                authoritiesString
                        );
                        
                        // For web clients, also set the cookie
                        tokenProvider.addCookieToResponse(response, accessToken);
                        
                        // Generate new refresh token
                        RefreshToken newRefreshToken = refreshTokenService.createRefreshToken(
                                user.getId(),
                                refreshTokenRequest.getDeviceId(),
                                null, // We don't have device type in refresh request
                                request.getRemoteAddr()
                        );
                        
                        // Get user details
                        UserDTO userDTO = userService.getUserById(user.getId());
                        
                        // Build response
                        JwtAuthResponse authResponse = JwtAuthResponse.builder()
                                .accessToken(accessToken)
                                .refreshToken(newRefreshToken.getToken())
                                .tokenType("Bearer")
                                .user(userDTO)
                                .expiresIn(tokenProvider.getAccessTokenExpiration())
                                .build();
                                
                        return ResponseEntity.ok(ApiResponse.success("Token refreshed successfully", authResponse));
                    })
                    .orElseThrow(() -> new BusinessException("Refresh token not found"));
        } catch (BusinessException e) {
            log.error("Token refresh failed", e);
            return ResponseEntity.status(HttpStatus.UNAUTHORIZED)
                    .body(ApiResponse.error(HttpStatus.UNAUTHORIZED.value(), e.getMessage()));
        } catch (Exception e) {
            log.error("Token refresh failed", e);
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
                    .body(ApiResponse.error(HttpStatus.INTERNAL_SERVER_ERROR.value(), "Token refresh failed: " + e.getMessage()));
        }
    }
    
    /**
     * Validates a token
     * GET /api/auth/validate
     *
     * @param token the token to validate
     * @return validation result
     */
    @GetMapping("/validate")
    public ResponseEntity<?> validateToken(@RequestParam String token) {
        boolean isValid = tokenProvider.validateToken(token);
        Map<String, Boolean> response = Map.of("valid", isValid);
        return ResponseEntity.ok(ApiResponse.success("Token validation", response));
    }
}
